require 'digest/sha1'

class User < ActiveRecord::Base
  belongs_to :group
  has_many :votes
  
  include Authentication
  include Authentication::ByPassword
  include Authentication::ByCookieToken

  validates_presence_of     :login
  validates_length_of       :login,    :within => 3..40
  validates_uniqueness_of   :login
  validates_format_of       :login,    :with => Authentication.login_regex, :message => Authentication.bad_login_message

  validates_format_of       :name,     :with => Authentication.name_regex,  :message => Authentication.bad_name_message, :allow_nil => true
  validates_length_of       :name,     :maximum => 100

  validates_presence_of     :email
  validates_length_of       :email,    :within => 6..100 #r@a.wk
  validates_uniqueness_of   :email
  validates_format_of       :email,    :with => Authentication.email_regex, :message => Authentication.bad_email_message

  validates_presence_of :group_id
  validates_presence_of :group_password
  before_create :make_activation_code 
  
  

  # HACK HACK HACK -- how to do attr_accessible from here?
  # prevents a user from submitting a crafted form that bypasses activation
  # anything else you want your user to change should be added here.
  attr_accessible :login, :email, :name, :password, :password_confirmation, :group_id, :group_password

  def is_amber?
    return email == "amber.feng@gmail.com"
  end
  
  def validate
		validate_group_authentication
  end
  
  def validate_group_authentication
    if(group_id != nil)
			real_password = Group.find(group_id).password
			if(group_password != real_password)
				errors.add(:group_password, "Incorrect group password")
			end
		end
  end
  
  # Activates the user in the database.
  def activate!
    @activated = true
    self.activated_at = Time.now.utc
    self.activation_code = nil
    save(false)
  end

  # Returns true if the user has just been activated.
  def recently_activated?
    @activated
  end

  def active?
    # the existence of an activation code means they have not activated yet
    activation_code.nil?
  end

  # Authenticates a user by their login name and unencrypted password.  Returns the user or nil.
  #
  # uff.  this is really an authorization, not authentication routine.  
  # We really need a Dispatch Chain here or something.
  # This will also let us return a human error message.
  #
  def self.authenticate(login, password)
    return nil if login.blank? || password.blank?
    u = find :first, :conditions => ['login = ? and activated_at IS NOT NULL', login] # need to get the salt
    u && u.authenticated?(password) ? u : nil
  end

  def login=(value)
    write_attribute :login, (value ? value.downcase : nil)
  end

  def email=(value)
    write_attribute :email, (value ? value.downcase : nil)
  end
  
  def calculate_positive
	  total = 0
  	as_payee = self.find_where_payee
  	for i in as_payee
  		total += i.amount
  	end
  	total
  end
  
  def calculate_negative
	  total = 0
  	as_payer = self.find_where_payer
  	for i in as_payer
  		total += i.amount
  	end
  	total
  end
  
  def find_where_payer(num = nil)
    if(num == nil)
      Individual.find(:all, :order=>"created_at DESC", :conditions => {:payer => self.id, :paid =>false})
    else
      Individual.find(:all, :order=>"created_at DESC", :conditions => {:payer => self.id, :paid =>false}, :limit=>num)
    end
	end
	
	def find_where_payee(num = nil)
		if(num == nil)
      Individual.find(:all, :order=>"created_at DESC", :conditions => {:payee => self.id, :paid =>false})
    else
      Individual.find(:all, :order=>"created_at DESC", :conditions => {:payee => self.id, :paid =>false}, :limit=>num)
    end
	end
	
	def find_expenses(num)
	  if(num < 0)
		  Expense.find(:all, :conditions => { :group => self.group }, :order=>"created_at DESC", :include => :user)
	  else
	    Expense.find(:all, :order=>"created_at DESC", :conditions => { :group_id => self.group }, :limit=>num, :include => :user)
    end
	end
  
  def individuals_owed_to(other)
		Individual.find(:all, :order=>"created_at DESC", :conditions => {:payer => self.id, :payee => other.id, :paid => false})
	end
  
  def owes(other)
    indv_owe_them = self.individuals_owed_to(other)
  	
  	owe_them = 0
  	for indv in indv_owe_them
  		owe_them += indv.amount
  	end
    owe_them
  end	
  
  def mark_payment_from(other, amount)
    puts "Marking payment from..."
    you_owe = self.owes(other)
  	they_owe = other.owes(self)
	
  	indv_they_owe = other.individuals_owed_to(self)
  	indv_you_owe = self.individuals_owed_to(other)
	
  	if they_owe > amount.to_i  #They pay you less than what they owe
  		total = they_owe - amount.to_i #They still owe you this much
		
  		#Clear all the individuals they owe you
  		for indv in indv_they_owe
  			indv.markaspaid
  		end
	
  		#Consolidation for them
  		indv = Individual.new(:payee => self.id, :payer => other.id, :amount => total, :paid => false, :con => true)
  		indv.paid = false
  		indv.save
  	elsif they_owe < amount.to_i #They pay you more than what they owe
  		total = you_owe + (amount.to_i - they_owe) #You now owe them this much
		
  		#clear all their indvs
  		for indv in indv_they_owe
  			indv.markaspaid
  		end
		
  		#clear all your indvs
  		for indv in indv_you_owe
  			indv.markaspaid
  		end
				
  		#consolidation for you
  		indv = Individual.create(:payee => other.id, :payer => self.id, :amount => total, :paid => false, :con => true)
  	else #they paid exactly what they owe, clear all of theirs
  		#clear all their indvs
  		for indv in indv_they_owe
  			markaspaid(indv)
  		end
  	end
  end
  
  def create_reset_code
 		@reset = true
 		self.reset_code =  Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
 		save(false)
 	end
 	
 	def delete_reset_code
		self.reset_code = nil
		save(false)
	end

  protected

  def make_activation_code
     self.activation_code = self.class.make_token
   end

	def recently_reset?
		@reset
	end

	


end
